package cn.chiship.sdk.framework.base;

import cn.chiship.sdk.core.base.BaseResult;
import cn.chiship.sdk.core.base.ProgressResult;
import cn.chiship.sdk.core.enums.BaseResultEnum;
import cn.chiship.sdk.core.base.constants.BaseConstants;
import cn.chiship.sdk.core.enums.FileExtEnum;
import cn.chiship.sdk.core.enums.ProgressResultEnum;
import cn.chiship.sdk.core.exception.custom.BusinessException;
import cn.chiship.sdk.core.exception.custom.SystemErrorException;
import cn.chiship.sdk.core.id.SnowflakeIdUtil;
import cn.chiship.sdk.core.util.*;
import cn.chiship.sdk.core.util.excel.ExcelFactory;
import cn.chiship.sdk.core.util.excel.core.ExcelEnum;
import cn.chiship.sdk.core.util.excel.core.ExcelService;
import cn.chiship.sdk.core.util.http.ResponseUtil;
import cn.chiship.sdk.framework.pojo.dto.export.ExportDto;
import cn.chiship.sdk.framework.pojo.dto.export.ExportTransferDataDto;
import cn.chiship.sdk.framework.pojo.dto.export.ImportDto;
import cn.chiship.sdk.framework.pojo.vo.PageVo;
import cn.chiship.sdk.framework.properties.ChishipDefaultProperties;
import cn.chiship.sdk.framework.util.ExportUtil;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.Page;
import com.github.pagehelper.page.PageMethod;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author lijian
 */
public abstract class BaseServiceImpl<Record, Example> implements BaseService<Record, Example> {

	@Autowired
	private BaseMapper<Record, Example> baseMapper;

	@Resource
	private ChishipDefaultProperties chishipDefaultProperties;

	/**
	 * 全局所有进度
	 */
	public ConcurrentHashMap<String, ProgressResult> processStatusHm;

	public BaseServiceImpl() {
		processStatusHm = new ConcurrentHashMap<>();
	}

	@Override
	public BaseResult insertSelective(Record record) {
		try {
			Class<?> clazz = record.getClass();
			Field id = clazz.getDeclaredField("id");
			/**
			 * id 为空,根据用户配置的生成原则进行生产 1.若主键类型为Long类型则自动使用雪花算法生成主键
			 * 2.若主键类型为String类型，则根据配置使用对应的生成原则 3.若业务层已经对主键赋值了，则忽略此配置 UUID 默认调用 @see
			 * cn.chiship.sdk.core.util.RandomUtil.uuidLowerCase() 方法 SNOWFLAKE 雪花算法 默认调用
			 * see cn.chiship.sdk.core.id.generateId() 方法
			 */
			String getId = "getId";
			String longs = "Long";
			String uuid = "UUID";
			String snowflake = "SNOWFLAKE";
			if (StringUtil.isNull(clazz.getMethod(getId).invoke(record))) {
				if (longs.equals(id.getType().getSimpleName())) {
					ClassUtil.setFieldValue(record, "id", SnowflakeIdUtil.generateId());
				}
				else {
					String tableKeyGenerator = chishipDefaultProperties.getKeyGenerator();
					if (uuid.equals(tableKeyGenerator)) {
						ClassUtil.setFieldValue(record, "id", RandomUtil.uuidLowerCase());
					}
					if (snowflake.equals(tableKeyGenerator)) {
						ClassUtil.setFieldValue(record, "id", SnowflakeIdUtil.generateStrId());
					}
				}
			}

			ClassUtil.setFieldValue(record, "gmtCreated", System.currentTimeMillis());
			ClassUtil.setFieldValue(record, "gmtModified", System.currentTimeMillis());
			ClassUtil.setFieldValue(record, "isDeleted", BaseConstants.NO);
			boolean flag = baseMapper.insertSelective(record) > 0;
			if (!flag) {
				return new BaseResult(Boolean.FALSE, BaseResultEnum.EXCEPTION_DATA_BASE_INSERT, null);
			}
			else {
				return new BaseResult(Boolean.TRUE, BaseResultEnum.SUCCESS, record);
			}
		}
		catch (Exception e) {
			throw new SystemErrorException(e.getCause());
		}
	}

	@Override
	public BaseResult deleteByExample(Example example) {
		try {
			baseMapper.deleteByExample(example);
			return new BaseResult(Boolean.TRUE, BaseResultEnum.SUCCESS, null);
		}
		catch (Exception e) {
			throw new SystemErrorException(e.getCause());
		}
	}

	@Override
	public BaseResult deleteByPrimaryKey(Object id) {
		try {
			baseMapper.deleteByPrimaryKey(id);
			return new BaseResult(Boolean.TRUE, BaseResultEnum.SUCCESS, null);
		}
		catch (Exception e) {
			throw new SystemErrorException(e.getCause());
		}
	}

	@Override
	public BaseResult updateByExampleSelective(Record record, Example example) {
		try {
			ClassUtil.setFieldValue(record, "gmtModified", System.currentTimeMillis());
			boolean flag = baseMapper.updateByExampleSelective(record, example) > 0;
			if (!flag) {
				return new BaseResult(Boolean.FALSE, BaseResultEnum.EXCEPTION_DATA_BASE_UPDATE, null);
			}
			else {
				return new BaseResult(Boolean.TRUE, BaseResultEnum.SUCCESS, record);
			}
		}
		catch (Exception e) {
			throw new SystemErrorException(e.getCause());
		}
	}

	@Override
	public BaseResult updateByPrimaryKeySelective(Record record) {
		try {
			ClassUtil.setFieldValue(record, "gmtModified", System.currentTimeMillis());
			boolean flag = baseMapper.updateByPrimaryKeySelective(record) > 0;
			if (!flag) {
				return new BaseResult(Boolean.FALSE, BaseResultEnum.EXCEPTION_DATA_BASE_UPDATE, null);
			}
			else {
				return new BaseResult(Boolean.TRUE, BaseResultEnum.SUCCESS, record);
			}
		}
		catch (Exception e) {
			throw new SystemErrorException(e.getCause());
		}

	}

	@Override
	public List<Record> selectByExample(Example example) {
		return baseMapper.selectByExample(example);
	}

	@Override
	public Record selectByPrimaryKey(Object id) {
		return baseMapper.selectByPrimaryKey(id);
	}

	@Override
	public BaseResult selectDetailsByPrimaryKey(Object id) {
		return BaseResult.error("请在【" + this.getClass().getSimpleName() + "】类中对方法【selectDetailsByPrimaryKey】重写,进行业务配置");
	}

	@Override
	public long countByExample(Example example) {
		return baseMapper.countByExample(example);
	}

	@Override
	public PageVo selectPageByExample(PageVo pageVo, Example example) {
		String sort = pageVo.getSort();
		PrintUtil.console("当前排序字段：" + sort);
		Page page = PageMethod.startPage(pageVo.getCurrent().intValue(), pageVo.getSize().intValue());
		PageMethod.orderBy(sort);
		List<Record> records = baseMapper.selectByExample(example);
		pageVo.setRecords(records);
		pageVo.setTotal(page.getTotal());
		pageVo.setPages(Long.valueOf(page.getPages()));
		return pageVo;
	}

	@Override
	public void exportData(HttpServletResponse response, ExportDto exportDto) throws Exception {
		String exportType = exportDto.getExportType();
		Boolean template = exportDto.getTemplate();
		ExportTransferDataDto exportTransferDataDto;
		if (Boolean.TRUE.equals(template)) {
			List<String> templates = Arrays.asList("xls", "xlsx");
			if (!templates.contains(exportType)) {
				ResponseUtil.writeJson(response, BaseResult.error("目前模板只支持xls及xlsx格式"));
			}
			else {
				exportTransferDataDto = downloadTemplate(exportType);
				ExportUtil.getInstance().exportByBytes(response, exportTransferDataDto);
			}

		}
		else {
			exportTransferDataDto = assemblyExportData(exportDto);
			exportTransferDataDto.setExportType(exportType);
			ExportUtil.getInstance().exportByType(response, exportTransferDataDto);
		}

	}

	@Override
	@Async
	public void asyncExportData(HttpServletResponse response, String taskId, ExportDto exportDto) {
		try {

			String exportType = exportDto.getExportType();
			Boolean template = exportDto.getTemplate();
			ExportTransferDataDto exportTransferDataDto = null;
			if (Boolean.TRUE.equals(template)) {
				List<String> templates = Arrays.asList("xls", "xlsx");
				if (!templates.contains(exportType)) {
					processStatusHm.put(taskId,
							ProgressResult.out(ProgressResultEnum.PROGRESS_ERROR_ENUM, "目前模板只支持xls及xlsx格式"));
					return;
				}
				exportTransferDataDto = downloadTemplate(exportType);
			}
			else {
				processStatusHm.put(taskId, ProgressResult.out(ProgressResultEnum.PROGRESS_SEARCH_DATABASE_ENUM));
				Thread.sleep(1000);
				exportTransferDataDto = assemblyExportData(exportDto);
				processStatusHm.put(taskId, ProgressResult.out(ProgressResultEnum.PROGRESS_DOING_ENUM,
						"查询出" + exportTransferDataDto.getTotal() + "条符合条件记录..."));
				Thread.sleep(1000);
				processStatusHm.put(taskId, ProgressResult.out(ProgressResultEnum.PROGRESS_FILE_ASSEMBLY_ENUM));
				exportTransferDataDto.setExportType(exportType);
				byte[] bytes = ExportUtil.getInstance().getExportBytesByType(exportTransferDataDto);
				exportTransferDataDto.setBytes(bytes);
			}
			exportTransferDataDto.setExportType(exportType);
			processStatusHm.put(taskId, ProgressResult.out(exportType, exportTransferDataDto.getFileName(),
					exportTransferDataDto.getBytes()));
		}
		catch (Exception e) {
			Thread.currentThread().interrupt();
			processStatusHm.put(taskId,
					ProgressResult.out(ProgressResultEnum.PROGRESS_ERROR_ENUM, e.getLocalizedMessage()));
		}
	}

	@Override
	public void getExportProcessStatus(HttpServletResponse response, String taskId) throws Exception {
		ProgressResult progressResult = processStatusHm.get(taskId);
		if (StringUtil.isNull(progressResult)) {
			ResponseUtil.writeJson(response, BaseResult.error("无效的任务ID！【" + taskId + "】"));
		}
		else {
			if (progressResult.isFile()) {
				byte[] bytes = progressResult.getBytes();
				if (StringUtil.isNull(bytes)) {
					processStatusHm.put(taskId,
							ProgressResult.out(ProgressResultEnum.PROGRESS_ERROR_ENUM, "标题列数与数值列数不匹配或不支持的导出格式！"));
				}
				else {
					processStatusHm.put(taskId, ProgressResult.out(ProgressResultEnum.PROGRESS_FINISH_ENUM));
					PrintUtil.console("文件大小:" + bytes.length);
					ExportTransferDataDto exportTransferDataDto = new ExportTransferDataDto(
							progressResult.getFileName(), bytes, progressResult.getExportType());
					ExportUtil.getInstance().exportByBytes(response, exportTransferDataDto);
					Thread.sleep(2000);
					processStatusHm.remove(taskId);
				}

			}
			else {
				ResponseUtil.writeJson(response, BaseResult.ok(progressResult));
			}
		}

	}

	@Override
	public ExportTransferDataDto assemblyExportData(ExportDto exportDto) {
		ExportTransferDataDto exportTransferDataDto = new ExportTransferDataDto(null, null, null, null, null, 0);
		// 下边两行代码纯属为了模拟报错
		if (true) {
			throw new BusinessException(
					"请在【" + this.getClass().getSimpleName() + "】类中对方法【assemblyExportData】重写,进行业务配置");
		}
		return exportTransferDataDto;
	}

	@Override
	public ExportTransferDataDto downloadTemplate(String exportType) {
		/**
		 * 以下仅作参考
		 */
		PrintUtil.console("您正在使用示例数据下载模板，建议在【" + this.getClass().getSimpleName() + "】类中对方法【downloadTemplate】重写,进行业务配置");
		List<String> headerNames = new ArrayList<>();
		List<List<String>> rows = new ArrayList<>();
		headerNames.add("标题1");
		headerNames.add("标题2");
		headerNames.add("标题3");
		List<String> values = new ArrayList<>();
		values.add("示例数据1");
		values.add("示例数据2");
		values.add("示例数据3");
		rows.add(values);
		ExcelService excelService = ExcelFactory.getExcelService(ExcelEnum.getExcelEnum(exportType));
		excelService.writeExcel("模板", "标题", 1, headerNames, rows);
		headerNames = new ArrayList<>();
		rows = new ArrayList<>();
		headerNames.add("备注");
		headerNames.add("说明");
		values = new ArrayList<>();
		values.add("示例数据1");
		values.add("示例数据2");
		rows.add(values);
		excelService.writeExcel("说明", "模板说明", 2, headerNames, rows);
		ByteArrayOutputStream os = new ByteArrayOutputStream();
		excelService.writeAndClose(os);
		return new ExportTransferDataDto("示例模板", os.toByteArray(), exportType);

	}

	@Override
	@Async
	public void asyncImportData(String taskId, InputStream inputStream, ImportDto importDto) {

		try {
			processStatusHm.put(taskId, ProgressResult.out(ProgressResultEnum.PROGRESS_DOING_ENUM));
			String fileExt = importDto.getFileName().substring(importDto.getFileName().lastIndexOf(".") + 1);
			ExcelEnum excelEnum = ExcelEnum.XLS;
			if (FileExtEnum.FILE_EXT_XLSX.getName().equals("." + fileExt)) {
				excelEnum = ExcelEnum.XLSX;
			}
			ExcelService excelService = ExcelFactory.getExcelService(excelEnum);
			excelService.load(inputStream);
			assemblyImportData(taskId, excelService, importDto);
		}
		catch (Exception e) {
			e.printStackTrace();
			processStatusHm.put(taskId,
					ProgressResult.out(ProgressResultEnum.PROGRESS_ERROR_ENUM, e.getLocalizedMessage()));
		}
	}

	@Override
	public void assemblyImportData(String taskId, ExcelService excelService, ImportDto importDto) {
		try {
			/*
			 * 以下代码均为举例说明 具体业务具体写
			 */
			PrintUtil.console(importDto);
			processStatusHm.put(taskId, ProgressResult.out(ProgressResultEnum.PROGRESS_PROGRESS_ENUM, "40%"));
			Thread.sleep(3000);
			processStatusHm.put(taskId, ProgressResult.out(ProgressResultEnum.PROGRESS_ERROR_ENUM,
					"请在【" + this.getClass().getSimpleName() + "】类中对方法【assemblyImportData】重写,进行业务配置"));
		}
		catch (Exception e) {
			Thread.currentThread().interrupt();
			processStatusHm.put(taskId,
					ProgressResult.out(ProgressResultEnum.PROGRESS_ERROR_ENUM, e.getLocalizedMessage()));
		}
	}

	@Override
	public BaseResult getImportProcessStatus(String taskId) {
		ProgressResult progressResult = processStatusHm.get(taskId);
		if (StringUtil.isNull(progressResult)) {
			return BaseResult.error("你开启了异步导入，确定调用【" + this.getClass().getSimpleName() + "】类中对方法【asyncExportData】了吗?");

		}
		return BaseResult.ok(progressResult);
	}

	@Override
	public BaseResult validateExistByField(String id, String field, String value) {
		/**
		 * 以下代码均为举例说明
		 */
		/*
		 * Example example=new **Example(); Example.Criteria
		 * criteria=example.createCriteria(); if (!StringUtil.isNullOrEmpty(id)){
		 * criteria.andIdNotEqualTo(id); } switch (field){ case "userName":
		 * criteria.andUserNameEqualTo(value); break; case "userCode":
		 * criteria.andUserCodeEqualTo(value); break; case "mobile":
		 * criteria.andMobileEqualTo(value); break; default: flag=false; } if (flag){
		 * return BaseResult.ok(**Mapper.countByExample(example)>0); }else{ return
		 * BaseResult.error(BaseResultEnum.FAILED,"暂未配置对字段"+field+"的校验"); }
		 */
		return BaseResult.error("请在【" + this.getClass().getSimpleName() + "】类中对方法【validateExistByField】重写,进行业务配置");
	}

	@Override
	public JSONObject assembleData(Record record) {
		throw new BusinessException("请在【" + this.getClass().getSimpleName() + "】类中对方法【assembleData】重写,进行业务配置");
	}

	@Override
	public List<JSONObject> assembleData(List<Record> records) {
		List<JSONObject> results = new ArrayList<>();
		if (records.isEmpty()) {
			return results;
		}
		for (Record record : records) {
			results.add(assembleData(record));
		}
		return results;
	}

}
